﻿using System.Collections.Generic;
using System.Configuration;
using System.Drawing;
using System.Runtime.InteropServices;
using PaintDotNet;
using PaintDotNet.Effects;
using PaintDotNet.IndirectUI;
using PaintDotNet.PropertySystem;

namespace AMPEffects.GaussianBlur
{
    [PluginSupportInfo(typeof(PluginSupportInfo), DisplayName = "(APM) Gaussian Blur")]
    public class GaussianBlurAmp : AMPEffects.DualPassTiledAmpEffectBase
    {
        [DllImport("AMPEffects.x64.dll", CallingConvention = CallingConvention.StdCall)]
        extern unsafe static void gaussian_blur(void* src, void* dst, int height, int width, int radius, Rectangle[] rois, int roisLength, bool isVerticalBlur);

        private int radius;
        private Dimensions blurDimensions;
        private bool isVert = false;

        private System.Diagnostics.Stopwatch tmr;

        public enum PropertyNames
        {
            Radius,
            BlurDimensions
        }

        public enum Dimensions
        {
            HorizontalAndVertical,
            HorizontalOnly,
            VerticalOnly
        }

        public static string StaticName
        {
            get
            {
                return "(AMP) Gaussian Blur";
            }
        }

        public static Bitmap StaticIcon
        {
            get
            {
                return new Bitmap(typeof(GaussianBlurAmp), "GaussianBlurIcon.png");
            }
        }

        public GaussianBlurAmp()
            : base(GaussianBlurAmp.StaticName, GaussianBlurAmp.StaticIcon, SubmenuNames.Blurs, PaintDotNet.Effects.EffectFlags.Configurable | PaintDotNet.Effects.EffectFlags.SingleThreaded)
        {
        }

        protected override ControlInfo OnCreateConfigUI(PropertyCollection props)
        {
            ControlInfo configUI = PropertyBasedEffect.CreateDefaultConfigUI(props);

            configUI.SetPropertyControlValue(PropertyNames.Radius, ControlInfoPropertyNames.DisplayName, "Radius");
            configUI.SetPropertyControlValue(PropertyNames.BlurDimensions, ControlInfoPropertyNames.DisplayName, "Blur Dimensions");
            PropertyControlInfo propInfo = configUI.FindControlForPropertyName(PropertyNames.BlurDimensions);
            propInfo.SetValueDisplayName(Dimensions.HorizontalAndVertical, "Horizontal and Vertical");
            propInfo.SetValueDisplayName(Dimensions.HorizontalOnly, "Horizontal Only");
            propInfo.SetValueDisplayName(Dimensions.VerticalOnly, "Vertical Only");

            return configUI;
        }

        protected override PropertyCollection OnCreatePropertyCollection()
        {
            List<Property> props = new List<Property>();

            props.Add(new Int32Property(PropertyNames.Radius, 2, 0, 200));
            props.Add(StaticListChoiceProperty.CreateForEnum<Dimensions>(PropertyNames.BlurDimensions, Dimensions.HorizontalAndVertical, false));

            return new PropertyCollection(props);
        }

        protected override void OnPreRender(RenderArgs dstArgs, RenderArgs srcArgs)
        {
            base.OnPreRender(dstArgs, srcArgs);

            if (base.IsInitialized)
            {
                // Control number of passes based on dimensions
                base.Passes = (this.blurDimensions == Dimensions.HorizontalAndVertical) ? 2 : 1;
                base.ApronSize = this.radius;
            }

            base.OnPreRenderComplete(dstArgs, srcArgs);
        }

        protected override void OnBeginPass(int pass, RenderArgs dstArgs, RenderArgs srcArgs)
        {
            if (pass == 1 && (this.blurDimensions == Dimensions.HorizontalAndVertical || this.blurDimensions == Dimensions.HorizontalOnly))
            {
                isVert = false;
            }
            else
            {
                isVert = true;
            }

            base.OnBeginPass(pass, dstArgs, srcArgs);
        }

        protected unsafe override void OnRenderRegion(Rectangle[] rois, RenderArgs dstArgs, RenderArgs srcArgs)
        {
            if (!this.IsInitialized) // || base.IsCancelRequested)
                return;

            Surface dst = dstArgs.Surface;
            Surface src = srcArgs.Surface;

            gaussian_blur(src.Scan0.VoidStar, dst.Scan0.VoidStar, src.Height, src.Width, this.radius, rois, rois.Length, isVert);

            if (this.tmr != null)
            {
                foreach (Rectangle rect in rois)
                {
                    if (rect.Top + rect.Height == src.Height &&
                        rect.Right == src.Width)
                    {
                        this.tmr.Stop();
                        System.Windows.Forms.MessageBox.Show(this.tmr.ElapsedMilliseconds.ToString() + "ms");
                    }
                }
            }
        }

        protected override void OnSetRenderInfo(PropertyBasedEffectConfigToken newToken, RenderArgs dstArgs, RenderArgs srcArgs)
        {
            this.radius = newToken.GetProperty<Int32Property>(PropertyNames.Radius).Value;
            this.blurDimensions = (Dimensions)newToken.GetProperty<StaticListChoiceProperty>(PropertyNames.BlurDimensions).Value;
            KeyValueConfigurationElement displayTimer = GetDllConfig().AppSettings.Settings["Timer"];

            if (displayTimer != null && displayTimer.Value == "1")
            {
                this.tmr = new System.Diagnostics.Stopwatch();
                this.tmr.Start();
            }

            base.OnSetRenderInfo(newToken, dstArgs, srcArgs);
        }
    }
}